package com.beiding.workordersystem.common.utils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class TreeUtils {


    public interface FindChildren<T> {
        Collection<T> findChildren(T o);
    }

    public interface GotCallBack<T> {
        void callback(T parent, T o);
    }

    private static GotCallBack emptyGotCallBack = (parent, o) -> {
    };

    public static <T> void fillChildren(T o, String childrenFiled, FindChildren<T> findChildren) {
        fillChildren(o, childrenFiled, findChildren, emptyGotCallBack);
    }

    public static <T> void fillChildren(T o, String childrenFiled, FindChildren<T> findChildren, GotCallBack<T> callBack) {
        try {
            String setter = "set" + childrenFiled.substring(0, 1).toUpperCase() + childrenFiled.substring(1);
            deep(o, setter, null, findChildren, callBack);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static <T> void deep(T o, String setter, Method m, FindChildren<T> findChildren, GotCallBack<T> callBack) throws InvocationTargetException, IllegalAccessException {

        Collection<T> children = findChildren.findChildren(o);

        if (children != null) {
            if (m == null) {
                for (Method method : o.getClass().getDeclaredMethods()) {
                    if (method.getName().equals(setter)) {
                        try {
                            method.invoke(o, children);
                            m = method;
                        }catch (IllegalAccessException | InvocationTargetException ignore) {
                        }
                    }
                }
                if (m == null) {
                    throw new RuntimeException("未找到Setter   " + setter + "(" + children.getClass() + ")");
                }
            } else {
                m.invoke(o, children);
            }

            if (children.size() > 0) {
                for (T child : children) {
                    callBack.callback(o, child);
                    deep(child, setter, m, findChildren, callBack);
                }
            }
        }

    }

    //不包含自身
    public static <T> List<T> childrenUnfold(T o, String childrenFiled) {
        try {
            Field declaredField = o.getClass().getDeclaredField(childrenFiled);
            declaredField.setAccessible(true);
            List<T> list = new ArrayList<>();
            deepGet(o, list, declaredField);
            return list;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static <T> void deepGet(T o, Collection<T> collection, Field declaredField) throws IllegalAccessException {
        Collection<T> children = (Collection<T>) declaredField.get(o);
        if (children != null && children.size() > 0) {
            for (T child : children) {
                collection.add(child);
                if (child != null) {
                    deepGet(child, collection, declaredField);
                }
            }
        }
    }

}
